/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Messaging;
using System.Collections;
using ELE.Common.Util;
using ELE.EnterpriseLibrary.Common.Logging;
using ELE.EnterpriseLibrary.Naming;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace ELE.EnterpriseLibrary.Messaging
{
  /// <summary>
  /// Summary description for MessageQueueingService.
  /// 
  /// Author : Miguel Curi
  /// 
  /// </summary>
  public class MessageQueuingService : LoggingObjectBase
  {
    private string mConfigurationResource;
    private MessagingConfiguration mConfiguration;
    private Hashtable mSinks;
    private Hashtable mSources;
    private INamingContext mContext;
    /// <summary>
    /// Create a new service based on the configuration from a given file 
    /// This will not initialize the service
    /// </summary>
    /// <param name="configuration"></param>
    public MessageQueuingService(string configuration) : this(configuration, true)
    {
    }
    /// <summary>
    /// Create a new service based on the configuration from a given file 
    /// and initialize it or not
    /// </summary>
    /// <param name="configuration"></param>
    /// <param name="init"></param>
    public MessageQueuingService(string configuration, bool init):this(configuration, init, null)
    {
    }
    /// <summary>
    /// Create a new service based on the configuration from a given file 
    /// This will not initialize the service
    /// </summary>
    /// <param name="configuration"></param>
    /// <param name="context"></param>
    public MessageQueuingService(string configuration, INamingContext context) : this(configuration, true, context)
    {
    }
    /// <summary>
    /// Constructor that takes a context
    /// </summary>
    /// <param name="configuration"></param>
    /// <param name="init"></param>
    /// <param name="context"></param>
    public MessageQueuingService(string configuration, bool init, INamingContext context)
    {
      mConfigurationResource = configuration;
      mContext = context;
      ReloadConfiguration(init);
    }
    /// <summary>
    /// Reloads the configuration from the file
    /// </summary>
    public void ReloadConfiguration()
    {
      ReloadConfiguration(false);
    }
    /// <summary>
    /// Reload the configuration and initialize or not
    /// </summary>
    /// <param name="init"></param>
    public void ReloadConfiguration(bool init)
    {
      if(mConfigurationResource != null)
      {
        mConfiguration = MessagingConfigurationParser.Parse(mConfigurationResource);
      }
      if(init)
      {
        Init();
      }
    }
    /// <summary>
    /// The location of the configuration resource for the message queueing service
    /// </summary>
    public string ConfigurationResource
    {
      get { return mConfigurationResource; }
      set { mConfigurationResource = value; }
    }
    /// <summary>
    /// Retrieve the array nof configured end points on this service
    /// </summary>
    public MessageEndPoint[] ConfiguredEndPoints
    {
      get
      {
        if(mConfiguration != null)
        {
          return mConfiguration.EndPoints;
        }
        return null;
      }
    }
    /// <summary>
    /// Returns the information about how an end point is configured
    /// </summary>
    /// <param name="serverName"></param>
    /// <returns></returns>
    public MessageEndPoint GetEndPointInfo(string serverName)
    {
      if(mConfiguration != null)
      {
        return mConfiguration.GetEndPoint(serverName);
      }
      return null;
    }
    /// <summary>
    /// Returns the information about how a message sink is configured
    /// </summary>
    /// <param name="sinkId"></param>
    /// <returns></returns>
    public MessageSinkInfo GetSinkInfo(string sinkId)
    {
      if(mConfiguration != null)
      {
        foreach(MessageEndPoint ep in mConfiguration.EndPoints)
        {
          MessageSinkInfo info = ep.GetMessageSinkInfo(sinkId);
          if(info != null)
          {
            return info;
          }
        }
      }
      return null;
    }
    /// <summary>
    /// Returns the information about how a message source is configured
    /// </summary>
    /// <param name="sourceId"></param>
    /// <returns></returns>
    public MessageSourceInfo GetSourceInfo(string sourceId)
    {
      if(mConfiguration != null)
      {
        foreach(MessageEndPoint ep in mConfiguration.EndPoints)
        {
          MessageSourceInfo info = ep.GetMessageSourceInfo(sourceId);
          if(info != null)
          {
            return info;
          }
        }
      }
      return null;
    }
    /// <summary>
    /// Initializes the service base on the configuration files
    /// </summary>
    public void Init()
    {
      if(mConfiguration != null)
      {
        foreach(MessageEndPoint endPoint in mConfiguration.EndPoints)
        {
          if(endPoint.Sinks != null && endPoint.Sinks.Length > 0)
          {
            foreach(MessageSinkInfo msi in endPoint.Sinks)
            {
              MessageSink sink = null;
              try
              {
                if(mContext == null)
                {
                  sink = new MessageSink(msi);
                }
                else
                {
                  sink = new MessageSink(msi, mContext);
                }
                this.AddSink(sink);
              }
              catch(NoProcessorDefinedException npde)
              {
                this.LogWarning("MessageQueuingService : Processor initialization failure for message sink " + msi.Id, npde);
              }
            }
          }
          if(endPoint.Sources != null && endPoint.Sources.Length > 0)
          {
            foreach(MessageSourceInfo msi in endPoint.Sources)
            {
              MessageSource source = new MessageSource(msi);
              AddSource(source);
            }
          }
        }
      }
    }
    /// <summary>
    /// Restart the messaging service
    /// </summary>
    public void Restart()
    {
      ReloadConfiguration();
      mSinks = null;
      mSources = null;
      Init();
    }
    /// <summary>
    /// Adds a sink to the list of message sinks in the service
    /// </summary>
    /// <param name="sink"></param>
    protected void AddSink(MessageSink sink)
    {
      if(mSinks == null)
      {
        mSinks = new Hashtable();
      }
      mSinks.Add(sink.SinkInfo.Id, sink);
    }
    /// <summary>
    /// Adds a sink to the list of message sinks in the service
    /// </summary>
    /// <param name="source"></param>
    protected void AddSource(MessageSource source)
    {
      if(mSources == null)
      {
        mSources = new Hashtable();
      }
      mSources.Add(source.SourceInfo.Id, source);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="messageBody"></param>
    /// <param name="queueName"></param>
    public void SendObjectMessage(object messageBody, string queueName)
    {
      this.SendObjectMessage(messageBody, queueName, null);
    }
    /// <summary>
    /// Send the object message to the queue
    /// </summary>
    /// <param name="messageBody"></param>
    /// <param name="queueName">This is the id of the queue in the Source tag of the configuration file</param>
    /// <param name="label"></param>
    public void SendObjectMessage(object messageBody, string queueName, string label)
    {
      MessageSource source = (MessageSource)mSources[queueName];
      if(source == null)
      {
        throw new InvalidMessageQueueException("Message queue named " + queueName + " not defined in configuration file");
      }
      source.SendObjectMessage(messageBody, label);
    }
    /// <summary>
    /// Lists all private queues in the defined endpoint. Note that end points are keyed by machine name.
    /// </summary>
    /// <param name="name">The name of the machine where MSMQ is to be queried</param>
    /// <returns>a list of message queues</returns>
    /// <exception cref="MessagingConfigurationException">
    /// If the endpoint cannot be located in the configuration file or 
    /// the configuration file could not be parsed
    /// </exception>
    /// <exception cref="MessageQueueException">Will be thrown if MSMQ is not setup on the endpoint</exception>
    public MessageQueue[] ListPrivateQueuesOnEndPoint(string name)
    {
      if(mConfiguration != null && mConfiguration.GetEndPoint(name) != null)
      {
        return MessageQueue.GetPrivateQueuesByMachine(name);
      }
      throw new MessagingConfigurationException("Either the configuration file could not be parsed, or the end point " + name + " is not defined properly in the configuration file");
    }
    /// <summary>
    /// Lists all public queues in the defined endpoint. Note that end points are keyed by machine name.
    /// </summary>
    /// <param name="name">The name of the machine where MSMQ is to be queried</param>
    /// <returns>a list of message queues</returns>
    /// <exception cref="MessagingConfigurationException">
    /// If the endpoint cannot be located in the configuration file or 
    /// the configuration file could not be parsed
    /// </exception>
    /// <exception cref="MessageQueueException">Will be thrown if MSMQ is not setup on the endpoint</exception>
    public MessageQueue[] ListPublicQueuesOnEndPoint(string name)
    {
      if(mConfiguration != null && mConfiguration.GetEndPoint(name) != null)
      {
        return MessageQueue.GetPublicQueuesByMachine(name);
      }
      throw new MessagingConfigurationException("Either the configuration file could not be parsed, or the end point " + name + " is not defined properly in the configuration file");
    }
  }
}
